from vBaum import *
from math import *
from random import *

voxelsPerUnit = 100

def voxelSize():
	return 1.0 / voxelsPerUnit

rand = Random()
rand.seed(12345)

def Vec3(x, y, z):
	return Vector3(x, z, y)

def randVec(sigma):
	x = rand.gauss(0, sigma)
	y = rand.gauss(0, sigma)
	z = rand.gauss(0, sigma)
	return Vec3(x,y,z)
	
def printVec(vec):
	print(str(vec.x) + " " + str(vec.y) + " " + str(vec.z))

#generate a fractal line
#first point always is (0,0,0)
#returns 2^(iOrder+1) + 1 points
def generateFractalLine(fSigma, fRoughness, iOrder):
	nFractalPoints = 2
	for i in range(iOrder):
		nFractalPoints = nFractalPoints * 2
	nFractalPoints = nFractalPoints + 1
#	print("fractal points:", nFractalPoints)
	
	fractalPoints = [Vector3(0,0,0)] * nFractalPoints
	iFracSpan = nFractalPoints - 1
	iStartPoint = iFracSpan // 2
	fractalPoints[iFracSpan] = randVec(fSigma)
	
	currMultiplier = 1.0
	while iFracSpan>1:
		currMultiplier = currMultiplier * fRoughness
#		print("fractal span=", iFracSpan)
		for i in range(nFractalPoints // iFracSpan):
			iReadIndex1 = i * iFracSpan
#			print("read index 1:", iReadIndex1)
			iReadIndex2 = iReadIndex1 + iFracSpan
#			print("read index 2:", iReadIndex2)
			vCenter = (fractalPoints[iReadIndex1] + fractalPoints[iReadIndex2]) * 0.5
			vCenter = vCenter + randVec(fSigma) * currMultiplier
			iWriteIndex = iStartPoint + i * iFracSpan
#			print("write index:", iWriteIndex)
			fractalPoints[iWriteIndex] = vCenter
		iFracSpan = iFracSpan // 2
		iStartPoint = iStartPoint // 2
		
	return fractalPoints

# rounds the subvolume of a volume that intersects with the given sphere
def roundSubVolume(volume, sphereCenter, sphereRadius, roundingRadius):
	sphere = Sphere(sphereRadius * voxelsPerUnit)
	sphere.setPosition(sphereCenter * voxelsPerUnit)
	intersection = volume.intersect(sphere)
#	intersection.exportAsObj("treeSphere", voxelsPerUnit, 0)
	volume = volume.subtract(sphere)
	rounded = intersection.roundConcave(roundingRadius * voxelsPerUnit)
#	rounded.exportAsObj("roundedTreeSphere", voxelsPerUnit, 0)
	volume = volume.union(rounded)

	return volume
	
def scalePoints(points):
	scaledPoints = []
	for p in points:
		scaledPoints.append(p * voxelsPerUnit)
	return scaledPoints

def createBranch(points, radiusBegin, radiusEnd):
	scaledPoints = scalePoints(points)
	scaledRadiusBegin = radiusBegin * voxelsPerUnit
	scaledRadiusEnd = radiusEnd * voxelsPerUnit
	print("generating spline", scaledRadiusBegin, scaledRadiusEnd)
	return Spline(scaledPoints, False, lambda x: Sphere(scaledRadiusBegin*(1-x) + scaledRadiusEnd*x))
	
def computeOrthogonalVector(vec):
	someVec = Vector3(1, 1, 1)
	if (abs(vec.x) >= abs(vec.y) and abs(vec.x) >= abs(vec.z)): someVec.x = 0
	elif (abs(vec.y) >= abs(vec.x) and abs(vec.y) >= abs(vec.z)): someVec.y = 0
	else: someVec.z = 0
	someVec.normalise()
	return someVec.crossProduct(vec)
	
def rotateDir(direction, rotateAxis, angle):
	q = Quaternion(angle, rotateAxis)
	return q * direction
	
def computeFirstBranchDir(rootDir, angle):
	ortho = computeOrthogonalVector(rootDir)
	return rotateDir(rootDir, ortho, angle)

def calcRadiusLengthConstant(designLength, designRadius, falloff):
	return designLength / pow(designRadius, falloff)
		
def diamToLength(radius, radiusLengthConstant, falloff):
	return pow(radius, falloff) * radiusLengthConstant

def LengthToDiam(maxLength, radiusLengthConstant, falloff):
	term = (maxLength / radiusLengthConstant)
	return pow(term, 1.0/falloff)

#generates heights of nodes in a branch
def generateNodePositions(length, initialInterNodeLength, interNodeLengthGrowth):
	currPos = length
	currInc = initialInterNodeLength
	positions = []
	
	while currPos > 0:
		positions.append(currPos)
		currPos -= currInc
		currInc += interNodeLengthGrowth
		
	#delete lowest branch
	if len(positions)>1:
		positions.pop();
	positions.reverse();
	return positions
	
#randomize all nodes except for the first
def randomizeNodesOnBranch(nodeVectors, nodePositions, nInterNodePoints, roughness, randomness):
	print("randomizing nodes on branch")
	print("nodePositions:", nodePositions)
	
	interNodeCutoff = nodePositions[0] / nInterNodePoints
	
	print("nodeVectors:")
	for position in nodeVectors:
		printVec(position)
	
	branchLength = nodePositions[-1]

	#uses 17 fractal points
	fractalPoints = generateFractalLine(1.0, roughness, 4)
	numFracPoints = len(fractalPoints)

	newNodeVectors = []
	branchContour = []
	
	currPos = 0
			
	for nodeIndex in range(0, len(nodeVectors)-1):
		nextVector = nodeVectors[nodeIndex + 1]
		currVector = nodeVectors[nodeIndex]
		currDirection = nextVector - currVector
		
		nodePos = nodePositions[nodeIndex]
		
		if interNodeCutoff > (currPos - nodePos):
			nInterNodePoints = 1
		
		for interPos in range(1, nInterNodePoints + 1):
			interNodePos = (nodePos - currPos) / nInterNodePoints * interPos + currPos
			samplePos = interNodePos * (numFracPoints-2) / branchLength
			sampleIndex = int(samplePos)
			alpha = samplePos - sampleIndex;
			sampleFracVector = fractalPoints[sampleIndex] * (1-alpha) + fractalPoints[sampleIndex + 1] * alpha
			
			vInterNodePoint = currVector + currDirection * (interPos / nInterNodePoints)
			
			branchVector = vInterNodePoint + sampleFracVector * branchLength * randomness
			branchContour.append(branchVector)
#			branchContour.append(vInterNodePoint)
			
			if interPos == nInterNodePoints:
#				newNodeVectors.append(branchVector)
				newNodeVectors.append(branchVector)
#				branchContour.append(branchVector)

		currPos = nodePos

#	newNodeVectors = [nodeVectors[0]] + newNodeVectors
	branchContour = [nodeVectors[0]] + branchContour
	newNodeVectors = [nodeVectors[0]] + newNodeVectors
	return newNodeVectors, branchContour

def generateNodesOnBranch(vFrom, vTo, nodePositions, nInterNodePoints, roughness, randomness):
	print("nodePositions:", nodePositions)

	
	branchLength = nodePositions[-1]
	nodeVectors = []
	currPos = 0
	
#	for nodeIndex in range(1, len(nodePositions)):
#		nodepos = nodePositions[nodeIndex]
	for nodepos in nodePositions:
		print("nodepos:", nodepos)
		nodePosition = vFrom + (vTo - vFrom) * nodepos / branchLength
		nodeVectors.append(nodePosition)

	nodeVectors = [vFrom] + nodeVectors
	
	print("nodeVectors:")
	for nodeVector in nodeVectors:
		printVec(nodeVector)
		
	return randomizeNodesOnBranch(nodeVectors, nodePositions, nInterNodePoints, roughness, randomness)

